package com.vision.sales.sv.report;

import java.awt.Color;
import java.text.DecimalFormat;
import java.text.FieldPosition;
import java.util.Date;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Map.Entry;

import net.sf.jasperreports.engine.JRDataSource;
import net.sf.jasperreports.engine.JRException;
import net.sf.jasperreports.engine.JasperPrint;
import ar.com.fdvs.dj.core.DynamicJasperHelper;
import ar.com.fdvs.dj.core.layout.ClassicLayoutManager;
import ar.com.fdvs.dj.core.layout.HorizontalBandAlignment;
import ar.com.fdvs.dj.core.layout.LayoutManager;
import ar.com.fdvs.dj.domain.AutoText;
import ar.com.fdvs.dj.domain.DJCalculation;
import ar.com.fdvs.dj.domain.DynamicReport;
import ar.com.fdvs.dj.domain.Style;
import ar.com.fdvs.dj.domain.builders.ColumnBuilder;
import ar.com.fdvs.dj.domain.builders.FastReportBuilder;
import ar.com.fdvs.dj.domain.builders.GroupBuilder;
import ar.com.fdvs.dj.domain.constants.Border;
import ar.com.fdvs.dj.domain.constants.Font;
import ar.com.fdvs.dj.domain.constants.GroupLayout;
import ar.com.fdvs.dj.domain.constants.HorizontalAlign;
import ar.com.fdvs.dj.domain.constants.Page;
import ar.com.fdvs.dj.domain.constants.VerticalAlign;
import ar.com.fdvs.dj.domain.entities.DJGroup;
import ar.com.fdvs.dj.domain.entities.DJGroupVariable;
import ar.com.fdvs.dj.domain.entities.columns.AbstractColumn;
import ar.com.fdvs.dj.domain.entities.columns.PropertyColumn;

import com.vision.core.cm.PageOrientation;
import com.vision.core.cm.PageSize;

/**
 * 
 * @author Mark
 *
 */
public class Report {
	
	public static final int MARGINS = 30;
	
	private static final Style subtitleStyle = new Style("style.subtitle");
	private static final Style headerStyle = new Style("style.header");
	private static final Style detailStyle = new Style("style.detail");
	private static final Style grandTotalStyle = new Style("style.total");
	private static final Style grandTotalAmountStyle = new Style("style.amounttotal");
	
	private static final Style amountHeaderStyle = new Style("style.amountheader");
	private static final Style amountDetailStyle = new Style("style.amountdetail");
	private static final Style amountTotalStyle = new Style("style.amounttotal");
	
	private static final Style groupStyle = new Style("style.group");
	private static final Style groupTotalAmountStyle = new Style("style.groupamounttotal");
	
	static {
		Font narrow = new Font(9, "Arial Narrow", false);
		Font groupHeaderFont = new Font(10, "Arial Narrow", false);
		Font grandTotalFont = new Font(10, "Arial Narrow", false);
		
		subtitleStyle.setTransparent(true);
		subtitleStyle.setVerticalAlign(VerticalAlign.TOP);
		subtitleStyle.setHorizontalAlign(HorizontalAlign.CENTER);
		subtitleStyle.setTextColor(Color.BLACK);
		subtitleStyle.setFont(Font.ARIAL_BIG_BOLD);
		
		headerStyle.setTransparent(true);
		headerStyle.setVerticalAlign(VerticalAlign.MIDDLE);
		headerStyle.setHorizontalAlign(HorizontalAlign.LEFT);
		headerStyle.setTextColor(Color.BLACK);
		headerStyle.setBorderBottom(Border.PEN_1_POINT());
		headerStyle.setFont(Font.ARIAL_MEDIUM_BOLD);
		
		detailStyle.setTransparent(true);
		detailStyle.setFont(narrow);
		detailStyle.setVerticalAlign(VerticalAlign.TOP);
		detailStyle.setHorizontalAlign(HorizontalAlign.LEFT);
		detailStyle.setTextColor(Color.BLACK);
		
		grandTotalStyle.setVerticalAlign(VerticalAlign.BOTTOM); // Bottom aligned
		grandTotalStyle.setFont(grandTotalFont);
		grandTotalStyle.setHorizontalAlign(HorizontalAlign.LEFT);
		
		grandTotalAmountStyle.setVerticalAlign(VerticalAlign.BOTTOM); // Bottom aligned
		grandTotalAmountStyle.setFont(grandTotalFont);
		grandTotalAmountStyle.setHorizontalAlign(HorizontalAlign.RIGHT);
		
		amountHeaderStyle.setTransparent(true);
		amountHeaderStyle.setVerticalAlign(VerticalAlign.MIDDLE);
		amountHeaderStyle.setHorizontalAlign(HorizontalAlign.RIGHT);
		amountHeaderStyle.setBorderBottom(Border.PEN_1_POINT());
		amountHeaderStyle.setFont(Font.ARIAL_MEDIUM_BOLD);

		amountDetailStyle.setVerticalAlign(VerticalAlign.TOP);
		amountDetailStyle.setHorizontalAlign(HorizontalAlign.RIGHT);
		amountDetailStyle.setFont(narrow);

		amountTotalStyle.setVerticalAlign(VerticalAlign.BOTTOM); // Bottom aligned
		amountTotalStyle.setFont(Font.ARIAL_MEDIUM_BOLD);
		amountTotalStyle.setHorizontalAlign(HorizontalAlign.RIGHT);
		
		groupStyle.setVerticalAlign(VerticalAlign.TOP);
		groupStyle.setBorderBottom(Border.PEN_1_POINT());
		groupStyle.setPaddingTop(15);
		groupStyle.setFont(groupHeaderFont);
		
		groupTotalAmountStyle.setVerticalAlign(VerticalAlign.BOTTOM); // Bottom aligned
		groupTotalAmountStyle.setFont(Font.ARIAL_MEDIUM_BOLD);
		groupTotalAmountStyle.setHorizontalAlign(HorizontalAlign.RIGHT);
	}
	
	private String filename;
	
	private PageSize size;
	private PageOrientation orientation;
	
	private String title;
	private String subtitle;
	
	private Map<String, AbstractColumn> columns;
	private Map<String, DJCalculation> totals;
	private Map<String, DJGroup> groups;
	
	private JRDataSource dataSource;
	
	private Map<String, DecimalFormat> amountFormatZeroIfEmptyMap = new HashMap<String, DecimalFormat>();
	
	// %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
	
	public JasperPrint asJasperPrint() throws JRException {
		FastReportBuilder fb = new FastReportBuilder();
		fb.setTitle(title);
		fb.setSubtitle(subtitle);
		fb.setSubtitleStyle(subtitleStyle);
		fb.setMargins(MARGINS, MARGINS, MARGINS, MARGINS);
		fb.setUseFullPageWidth(true);
		fb.setWhenNoData("There is no data to display.", null, true, true);
		fb.setPageSizeAndOrientation(getPage());

		for (Entry<String, AbstractColumn> e : columns.entrySet()) {
			PropertyColumn c = (PropertyColumn) e.getValue();
			fb.addColumn(c);
		}
		
		if (groups != null) {
			for (Entry<String, DJGroup> e : groups.entrySet()) {
				DJGroup g = e.getValue();
				fb.addGroup(g);
			}
		}
		
		if (totals != null) {
			for (Entry<String, DJCalculation> e : totals.entrySet()) {
				AbstractColumn col = getColumns().get(e.getKey());
				if (col != null) {
					fb.addGlobalFooterVariable(col, e.getValue(), grandTotalAmountStyle);
				}
			}

			fb.setGrandTotalLegendStyle(grandTotalStyle);
			fb.setGrandTotalLegend("Total");
			fb.setGlobalFooterVariableHeight(25);
		}
		
		AutoText at = new AutoText(AutoText.AUTOTEXT_PAGE_X_OF_Y, AutoText.POSITION_FOOTER, HorizontalBandAlignment.RIGHT);
		fb.addAutoText(at);
		
		DynamicReport dr = fb.build();
		LayoutManager layout = new ClassicLayoutManager();
		JasperPrint print = DynamicJasperHelper.generateJasperPrint(dr, layout, dataSource);
		return print;
	}
	
	public Page getPage() {
		if (size == PageSize.A4 && orientation == PageOrientation.PORTRAIT)
			return Page.Page_A4_Portrait();
		else if (size == PageSize.A4 && orientation == PageOrientation.LANDSCAPE)
			return Page.Page_A4_Landscape();
		else if (size == PageSize.LEGAL && orientation == PageOrientation.PORTRAIT)
			return Page.Page_Legal_Portrait();
		else if (size == PageSize.LEGAL && orientation == PageOrientation.LANDSCAPE)
			return Page.Page_Legal_Landscape();
		else if (size == PageSize.LETTER && orientation == PageOrientation.PORTRAIT)
			return Page.Page_Letter_Portrait();
		
		// if (size == PaperSize.LETTER && orientation == PageOrientation.LANDSCAPE)
			
		return Page.Page_Letter_Landscape();
	}
	
	public void addColumn(String id, String header, Class<?> clazz, int width, Style headerStyle, Style style, String pattern, DJCalculation calc) {
		ColumnBuilder cb = new ColumnBuilder();
		cb.setColumnProperty(id, clazz);
		cb.setTitle(header);
		cb.setWidth(width);
		cb.setHeaderStyle(headerStyle);
		cb.setStyle(style);
		cb.setPattern(pattern);
		if (pattern != null && isNumeric(clazz))
			cb.setTextFormatter( getAmountFormatZeroIfEmpty(pattern) );
			
		AbstractColumn col = cb.build();
		getColumns().put(id, col);
		
		if (calc != null)
			getTotals().put(id, calc);
	}
	
	public void addColumn(String id, String header, Class<?> clazz, int width, Style headerStyle, Style style, String pattern) {
		addColumn(id, header, clazz, width, headerStyle, style, pattern, null);
	}
	
	public void addDateColumn(String id, String header, int width, Style headerStyle, Style style, String pattern) {
		addColumn(id, header, Date.class, width, headerStyle, style, pattern);
	}
	
	public void addDateColumn(String id, String header, int width, String pattern) {
		addDateColumn(id, header, width, headerStyle, detailStyle, pattern);
	}
	
	public void addStringColumn(String id, String header, int width, Style headerStyle, Style style, String pattern) {
		addColumn(id, header, String.class, width, headerStyle, style, pattern);
	}
	
	public void addStringColumn(String id, String header, int width, String pattern) {
		addStringColumn(id, header, width, headerStyle, detailStyle, pattern);
	}
	
	public void addIntColumn(String id, String header, int width, Style headerStyle, Style style, String pattern) {
		addColumn(id, header, Integer.class, width, headerStyle, style, pattern);
	}
	
	public void addIntColumn(String id, String header, int width) {
		addIntColumn(id, header, width, headerStyle, detailStyle, "#,##0.00");
	}
	
	public void addIntColumn(String id, String header, int width, String pattern) {
		addIntColumn(id, header, width, headerStyle, detailStyle, pattern);
	}
	
	public void addDoubleColumn(String id, String header, int width) {
		addDoubleColumn(id, header, width, amountHeaderStyle, amountDetailStyle, "#,##0.00", null);
	}
	
	public void addDoubleColumn(String id, String header, int width, DJCalculation calc) {
		addDoubleColumn(id, header, width, amountHeaderStyle, amountDetailStyle, "#,##0.00", calc);
	}
	
	public void addDoubleColumn(String id, String header, int width, String pattern, DJCalculation calc) {
		addDoubleColumn(id, header, width, amountHeaderStyle, amountDetailStyle, pattern, calc);
	}
	
	public void addDoubleColumn(String id, String header, int width, Style headerStyle, Style style, String pattern, DJCalculation calc) {
		addColumn(id, header, Double.class, width, headerStyle, style, pattern, calc);
	}
	
	public void addGroup(String id) {
		addGroup(id, GroupLayout.VALUE_IN_HEADER);
	}
	
	public void addGroup(String id, GroupLayout layout) {
		PropertyColumn col = (PropertyColumn) getColumns().get(id);
		col.setStyle(groupStyle);
		
		GroupBuilder gb = new GroupBuilder();
		gb.setCriteriaColumn(col);
		gb.setGroupLayout(layout);
		gb.setHeaderHeight(15);
		
		DJGroup group = gb.build();
		
		getGroups().put(id, group);
	}
	
	public void addGroupFooter(String groupId, String col, DJCalculation calc) {
		DJGroup group = getGroups().get(groupId);
		if (group == null)
			return;
		
		AbstractColumn column = getColumns().get(col);
		if (column == null)
			return;
		
		DJGroupVariable var = new DJGroupVariable(column, calc);
		var.setStyle(groupTotalAmountStyle);
		group.addFooterVariable(var);
	}
	
	public void addFooter(String footer) {
		
	}
	
	// %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
	
	public String getFilename() {
		return filename;
	}

	public void setFilename(String filename) {
		this.filename = filename;
	}
	
	public PageSize getSize() {
		return size;
	}
	
	public void setSize(PageSize size) {
		this.size = size;
	}
	
	public PageOrientation getOrientation() {
		return orientation;
	}
	
	public void setOrientation(PageOrientation orientation) {
		this.orientation = orientation;
	}
	
	public String getTitle() {
		return title;
	}

	public void setTitle(String title) {
		this.title = title;
	}

	public String getSubtitle() {
		return subtitle;
	}

	public void setSubtitle(String subtitle) {
		this.subtitle = subtitle;
	}

	public Map<String, AbstractColumn> getColumns() {
		if (columns == null) columns = new LinkedHashMap<String, AbstractColumn>();
		return columns;
	}

	public void setColumns(Map<String, AbstractColumn> columns) {
		this.columns = columns;
	}

	public Map<String, DJGroup> getGroups() {
		if (groups == null) groups = new LinkedHashMap<String, DJGroup>();
		return groups;
	}
	
	public Map<String, DJCalculation> getTotals() {
		if (totals == null) totals = new LinkedHashMap<String, DJCalculation>();
		return totals;
	}

	public void setGroups(Map<String, DJGroup> groups) {
		this.groups = groups;
	}

	public JRDataSource getDataSource() {
		return dataSource;
	}

	public void setDataSource(JRDataSource dataSource) {
		this.dataSource = dataSource;
	}
	
	// %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
	
	private DecimalFormat getAmountFormatZeroIfEmpty(String pattern) {
		DecimalFormat fmt = amountFormatZeroIfEmptyMap.get(pattern);
		if (fmt == null) {
			fmt = new DecimalFormat(pattern) {
				private static final long serialVersionUID = 1L;
				@Override
				public StringBuffer format(double value, StringBuffer sb, FieldPosition pos) {
					if (value == 0) {
						return sb;
					}
					return super.format(value, sb, pos);
				}
			};
			amountFormatZeroIfEmptyMap.put(pattern, fmt);
		}
		return fmt;
	}
	
	static boolean isNumeric(Class<?> clazz) {
		return Number.class.isAssignableFrom(clazz);
	}
	
}
